/*
 * @Author: OreoWang
 * @Email: ihc523@163.com
 * @Date: 2020-06-01 12:05:23
 * @LastEditors: OreoWang
 * @LastEditTime: 2022-08-17 15:17:09
 * @Description: 节点自动属性装饰器
 */

import { EDITOR } from "cc/env";
import { isValid, Component, Node, js, VERSION } from "cc";

import { logger } from "./ViewLogger";
import { isSupportRuntime } from "./ViewTool";

export type TypeProperty = Node | Component | Node[] | Component[];

/**
 * 属性自动访问器
 * @param path 子节点路径（字符串或数组）
 * @param component 组件
 * @returns 
 */
export const _access = function (path: string | Array<string>, component?: typeof Component) {
	/**
	 * 属性装饰器
	 * @param target 
	 * @param key 
	 * @description 将属性定义为getter形式
	 * https://github.com/wycats/javascript-decorators/issues/18
	 */
	function decorator(target: any, key: string): any {
		return {
			get: function () {
				let self: Component = this as Component;
				let _value = getValue(self, key);
				if (typeof _value === 'undefined') {
					if (key == "node") {
						logger.error("ViewNode", "子节点不能命名为 'node' ，因为其与组件原有属性冲突");
						_value = self.node;
					}
					else {
						if (typeof path == "string") {
							_value = targetHandler(self.node, path, component);
						}
						else if (path instanceof Array) {
							let values: Array<any> = [];
							let maps: any = {};
							path.forEach(p => {
								//过滤重复路径
								if(!maps[p]){
									maps[p] = 1;
									let v = targetArrayHandler(self.node, p, component);
									values = values.concat(v);
								}
							})
							_value = values;
						}
					}
					setValue(self, key, _value);
				}
				return _value;
			},
			set: function (value: any) { //当 value=undefined 或 value=null 时，getter 会重新获取
				setValue(this, key, value);
			},
			enumerable: true,
			configurable: false
		};
	}
	return decorator;
}

function getValue(target: any, key: string) {
	if (!target.__autoProperty__) {
		target.__autoProperty__ = new Map();
		logger.error("使用 access 获取节点属性，组件需要继承 ViewNode 基类");
	}
	let map: Map<string, TypeProperty> = target.__autoProperty__;
	let value = map.get(key) || undefined;
	if (value && typeof value === 'object') {
		if (!isValid(value)) {
			map.delete(key);
			value = undefined;
		}
	}
	return value;
}
function setValue(target: any, key: string, value: any) {
	if (!target.__autoProperty__) {
		target.__autoProperty__ = new Map();
		logger.error("使用 access 获取节点属性，组件需要继承 ViewNode 基类");
	}
	target.__autoProperty__.set(key, value);
}

function getAnyChildByPath(parent: any, path: string, findAll: boolean) {
	const segments = path.split('/');
	// eslint-disable-next-line @typescript-eslint/no-this-alias
	let lastNode: Node = parent;
	for (let i = 0; i < segments.length; ++i) {
		const segment = segments[i];
		if (segment.length === 0) {
			continue;
		}
		let next: Node = null!;
		if (i == segments.length - 1) {
			if (findAll) {
				return lastNode.children.filter((childNode) => childNode.name === segment)!;
			}
			else {
				return lastNode.children.find((childNode) => childNode.name === segment)!;
			}
		}
		else {
			next = lastNode.children.find((childNode) => childNode.name === segment)!;
		}

		if (!next) {
			if (findAll) {
				return [];
			}
			return null;
		}
		lastNode = next;
	}
	return lastNode;
}
function getAllChildByPath(parent: any, path: string) {
	if (!isSupportRuntime() || !parent) return [];

	let list = (getAnyChildByPath(parent, path, true) || []) as Array<Node>;
	return list;
}
function getChildByPath(parent: any, path: string) {
	if (!isSupportRuntime() || !parent) return undefined;

	let node: Node = getAnyChildByPath(parent, path, false) as Node || null;
	return node;
}

function targetHandler(parent: any, path: string, component?: typeof Component) {
	let node = getChildByPath(parent, path)!;
	if(!node){
		logger.error(`未找到节点: path='${path}'`);
	}
	else if (component) {
		let value: Component = node.getComponent(component)!;
		if(!value){
			logger.error(`未找到组件: '${js.getClassName(component)}', path='${path}'`);
		}
		return value;
	}
	return node;
}
function targetArrayHandler(parent: any, path: string, component?: typeof Component) {
	let nodes = getAllChildByPath(parent, path)!;
	if(!nodes || nodes.length==0){
		logger.error(`未找到节点: path='${path}'`);
	}
	if (component) {
		let values: Array<Component> = [];
		nodes.forEach((node) => {
			let value = node.getComponent(component)!;
			if(!value){
				logger.error(`未找到组件: '${js.getClassName(component)}', path='${path}'`);
			}
			values.push(value);
		})
		return values;
	}
	return nodes;
}
